package com.gdth.coder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;

import com.gdth.utils.ArrayUtils;
import com.gdth.utils.StringUtils;

/** 
 * @author  Arvin
 * @version 2016-6-3 11:58:47 
 * 
 */
public class CodeUtil {
	public static final String KEY_ALGORITHM = "RSA";
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

	/**
	 *  使用私钥解密
	 * @param content
	 * @param private_key
	 * @param input_charset
	 * @return
	 * @throws Exception
	 */
	public static String decrypt(String content, String privateKey) {
		PrivateKey prikey;
		Cipher cipher = null;
		try {
			prikey = getPrivateKey(privateKey);
			cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.DECRYPT_MODE, prikey);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		InputStream ins = new ByteArrayInputStream(Base64.decode(content));
		ByteArrayOutputStream writer = new ByteArrayOutputStream();
		//一次全部读取，注意大小不能超过1024
		byte[] buf = new byte[1024];
		int bufl;

		try {
			while ((bufl = ins.read(buf)) != -1) {
				byte[] block = null;

				if (buf.length == bufl) {
					block = buf;
				} else {
					block = new byte[bufl];
					for (int i = 0; i < bufl; i++) {
						block[i] = buf[i];
					}
				}
				writer.write(cipher.doFinal(block));
			}
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			return new String(writer.toByteArray(), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static String decryptForLong(String content, String privateKey) {
		PrivateKey prikey;
		Cipher cipher = null;
		try {
			prikey = getPrivateKey(privateKey);
			cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.DECRYPT_MODE, prikey);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// 解密时超过128字节就报错。为此采用分段解密的办法来解密
		byte[] data = Base64.decode(content);
		StringBuilder sb = new StringBuilder();
//		System.out.println(data.length);
		try {
			for (int i = 0; i < data.length; i += 256) {
				byte[] doFinal;
				doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + 256));
				sb.append(new String(doFinal, "utf-8"));
			}
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return sb.toString();
	}

	public static String encryptByPublic(String content, String publicKey) {
		try {
			PublicKey pubkey = getPublicKeyFromX509(publicKey);

			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, pubkey);

			byte plaintext[] = content.getBytes("UTF-8");
		
			byte[] output = cipher.doFinal(plaintext);
		
			return Base64.encode(output);

		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * 获得私钥
	 * @param key 私钥
	 * @throws Exception
	 */
	public static PrivateKey getPrivateKey(String key) throws Exception {

		byte[] keyBytes;

		keyBytes = Base64.decode(key);

		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

		KeyFactory keyFactory = KeyFactory.getInstance("RSA");

		PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
		
		return privateKey;
	}

	/**
	 * 得到公钥
	 * 
	 * @param algorithm
	 * @param bysKey
	 * @return
	 */
	private static PublicKey getPublicKeyFromX509(String bysKey) throws NoSuchAlgorithmException, Exception {
		byte[] decodedKey = Base64.decode(bysKey);
		X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);

		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		return keyFactory.generatePublic(x509);
	}

	/**
	 * 使用公钥加密
	 * 
	 * @param content 密文
	 * @param key 公钥
	 * @return
	 */
	public static String encryptByPublicForLong(String content, String publicKey) {
		try {
			PublicKey pubkey = getPublicKeyFromX509(publicKey);

			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, pubkey);

			StringBuilder sb = new StringBuilder();
			byte[] dataReturn = null;
			for (int i = 0; i < content.length(); i += 50){
				String data = StringUtils.substring(content, i, i + 50);
				byte[] plaintext = data.getBytes("UTF-8");
//				System.out.println("加密"+plaintext.length + " ...." +new String(plaintext));
				byte[] doFinal = cipher.doFinal(plaintext);  
				sb.append(new String(doFinal));  
				dataReturn = ArrayUtils.addAll(dataReturn, doFinal);  
			}
			return new String(Base64.encode(dataReturn));

		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * 根据私有钥匙生成签名
	 * @param encodedData
	 * @return
	 */
	public static String newSign(String encodedData, String privateKey){
		String sign = null;
		try {
			sign = sign(encodedData.getBytes(), privateKey);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return sign;
	} 
	
	/**
	 * 验证签名是否合法
	 * @param encodedData
	 * @param publicKey
	 * @return
	 */
	public static boolean isVerify(String encodedData, String sign, String publicKey){
		boolean status = false;
		try {
			status = verify(encodedData.getBytes(), publicKey, sign);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return status;
	}
	
	/**
	 * 用私钥对信息生成数字签名
	 * @param data 加密数据
	 * @param privateKey 私钥
	 * @return
	 * @throws Exception
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		// 解密由base64编码的私钥
		byte[] keyBytes = decryptBASE64(privateKey);

		// 构造PKCS8EncodedKeySpec对象
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

		// KEY_ALGORITHM 指定的加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

		// 取私钥匙对象
		PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

		// 用私钥对信息生成数字签名
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(priKey);
		signature.update(data);

		return encryptBASE64(signature.sign());
	}

	/**
	 * 校验数字签名
	 * 
	 * @param data
	 *            加密数据
	 * @param publicKey
	 *            公钥
	 * @param sign
	 *            数字签名
	 * 
	 * @return 校验成功返回true 失败返回false
	 * @throws Exception
	 * 
	 */
	public static boolean verify(byte[] data, String publicKey, String sign)
			throws Exception {

		// 解密由base64编码的公钥
		byte[] keyBytes = decryptBASE64(publicKey);

		// 构造X509EncodedKeySpec对象
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

		// KEY_ALGORITHM 指定的加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

		// 取公钥匙对象
		PublicKey pubKey = keyFactory.generatePublic(keySpec);

		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(pubKey);
		signature.update(data);

		// 验证签名是否正常
		return signature.verify(decryptBASE64(sign));
	}
	
	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}
	
	public static List<String> newPrivateKey(){
		KeyPairGenerator keyPairGen = null;
		try {
			keyPairGen = KeyPairGenerator.getInstance("RSA");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
        // 初始化密钥对生成器，密钥大小为1024位  
        keyPairGen.initialize(2048);  
        // 生成一个密钥对，保存在keyPair中  
        KeyPair keyPair = keyPairGen.generateKeyPair();  
  
        // 得到私钥  
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        
        // 得到公钥 
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();   
  
        List<String> keyList = new ArrayList<String>();
        String key = null;
		try {
			key = encryptBASE64(privateKey.getEncoded());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        keyList.add(key);
        try {
			keyList.add(encryptBASE64(publicKey.getEncoded()));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
        return keyList; 
	}
}
